/*
 * Copyright (C) 2014 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package okhttp3;

import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static okhttp3.internal.Util.closeQuietly;
import static okhttp3.internal.platform.Platform.INFO;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;

import javax.annotation.Nullable;

import okhttp3.internal.NamedRunnable;
import okhttp3.internal.cache.CacheInterceptor;
import okhttp3.internal.connection.ConnectInterceptor;
import okhttp3.internal.connection.StreamAllocation;
import okhttp3.internal.http.BridgeInterceptor;
import okhttp3.internal.http.CallServerInterceptor;
import okhttp3.internal.http.RealInterceptorChain;
import okhttp3.internal.http.RetryAndFollowUpInterceptor;
import okhttp3.internal.platform.Platform;
import okio.AsyncTimeout;
import okio.Timeout;

/**
 * RealCall实现了Call接口，它封装了请求的调用，这个构造函数的逻辑也很简单：赋值外部传入的OkHttpClient、Request与forWebSocket，
 * 并创建了重试与重定向拦截器RetryAndFollowUpInterceptor。
 *
 * @author chenli
 *
 */
final class RealCall implements Call {
	final OkHttpClient client;
	final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
	final AsyncTimeout timeout;

	/**
	 * There is a cycle between the {@link Call} and {@link EventListener} that
	 * makes this awkward. This will be set after we create the call instance then
	 * create the event listener instance.
	 */
	private @Nullable EventListener eventListener;

	/**
	 * The application's original request unadulterated by redirects or auth
	 * headers.
	 */
	final Request originalRequest;
	final boolean forWebSocket;

	// Guarded by this.
	private boolean executed;

	private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
		// 我们构建的OkHttpClient，用来传递参数
		this.client = client;
		this.originalRequest = originalRequest;
		// 是不是WebSocket请求，WebSocket是用来建立长连接的，后面我们会说
		this.forWebSocket = forWebSocket;
		// 构建RetryAndFollowUpInterceptor拦截器
		this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
		this.timeout = new AsyncTimeout() {
			@Override
			protected void timedOut() {
				cancel();
			}
		};
		this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
	}

	static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
		// Safely publish the Call instance to the EventListener.
		RealCall call = new RealCall(client, originalRequest, forWebSocket);
		call.eventListener = client.eventListenerFactory().create(call);
		return call;
	}

	@Override
	public Request request() {
		return originalRequest;
	}

	@Override
	public Response execute() throws IOException {
		synchronized (this) {
			if (executed)
				throw new IllegalStateException("Already Executed");
			executed = true;
		}
		captureCallStackTrace();
		timeout.enter();
		eventListener.callStart(this);
		try {
			// 这里我们会用 client 对象（实际也就是上面创建 RealCall 的时候传入的 OkHttpClient）的 dispatcher() 方法来获取一个 Dispatcher 对象，并调用它的 executed() 方法来将当前的 RealCall 加入到一个双端队列中
			client.dispatcher().executed(this);
			Response result = getResponseWithInterceptorChain();
			if (result == null)
				throw new IOException("Canceled");
			return result;
		} catch (IOException e) {
			e = timeoutExit(e);
			eventListener.callFailed(this, e);
			throw e;
		} finally {
			client.dispatcher().finished(this);
		}
	}

	@Nullable
	IOException timeoutExit(@Nullable IOException cause) {
		if (!timeout.exit())
			return cause;

		InterruptedIOException e = new InterruptedIOException("timeout");
		if (cause != null) {
			e.initCause(cause);
		}
		return e;
	}

	private void captureCallStackTrace() {
		Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
		retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
	}

	@Override
	public void enqueue(Callback responseCallback) {
		synchronized (this) {
			if (executed)
				throw new IllegalStateException("Already Executed");
			executed = true;
		}
		captureCallStackTrace();
		eventListener.callStart(this);
		client.dispatcher().enqueue(new AsyncCall(responseCallback));
	}

	@Override
	public void cancel() {
		retryAndFollowUpInterceptor.cancel();
	}

	@Override
	public Timeout timeout() {
		return timeout;
	}

	@Override
	public synchronized boolean isExecuted() {
		return executed;
	}

	@Override
	public boolean isCanceled() {
		return retryAndFollowUpInterceptor.isCanceled();
	}

	@SuppressWarnings("CloneDoesntCallSuperClone") // We are a final type & this saves clearing state.
	@Override
	public RealCall clone() {
		return RealCall.newRealCall(client, originalRequest, forWebSocket);
	}

	StreamAllocation streamAllocation() {
		return retryAndFollowUpInterceptor.streamAllocation();
	}

	final class AsyncCall extends NamedRunnable {
		private final Callback responseCallback;

		AsyncCall(Callback responseCallback) {
			super("OkHttp %s", redactedUrl());
			this.responseCallback = responseCallback;
		}

		String host() {
			return originalRequest.url().host();
		}

		Request request() {
			return originalRequest;
		}

		RealCall get() {
			return RealCall.this;
		}

		/**
		 * Attempt to enqueue this async call on {@code executorService}. This will
		 * attempt to clean up if the executor has been shut down by reporting the call
		 * as failed.
		 */
		void executeOn(ExecutorService executorService) {
			assert (!Thread.holdsLock(client.dispatcher()));
			boolean success = false;
			try {
				executorService.execute(this);
				success = true;
			} catch (RejectedExecutionException e) {
				InterruptedIOException ioException = new InterruptedIOException("executor rejected");
				ioException.initCause(e);
				eventListener.callFailed(RealCall.this, ioException);
				responseCallback.onFailure(RealCall.this, ioException);
			} finally {
				if (!success) {
					client.dispatcher().finished(this); // This call is no longer running!
				}
			}
		}

		@Override
		protected void execute() {
			boolean signalledCallback = false;
			timeout.enter();
			try {
				Response response = getResponseWithInterceptorChain();
				signalledCallback = true;
				responseCallback.onResponse(RealCall.this, response);
			} catch (IOException e) {
				e = timeoutExit(e);
				if (signalledCallback) {
					// Do not signal the callback twice!
					Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
				} else {
					eventListener.callFailed(RealCall.this, e);
					responseCallback.onFailure(RealCall.this, e);
				}
			} catch (Throwable t) {
				cancel();
				if (!signalledCallback) {
					IOException canceledException = new IOException("canceled due to " + t);
					responseCallback.onFailure(RealCall.this, canceledException);
				}
				throw t;
			} finally {
				client.dispatcher().finished(this);
			}
		}
	}

	/**
	 * Returns a string that describes this call. Doesn't include a full URL as that
	 * might contain sensitive information.
	 */
	String toLoggableString() {
		return (isCanceled() ? "canceled " : "") + (forWebSocket ? "web socket" : "call") + " to " + redactedUrl();
	}

	String redactedUrl() {
		return originalRequest.url().redact();
	}

	/**
	 * 首先一次将
	 * 用户添加的拦截器client.interceptors()--retryAndFollowUpInterceptor（重定向）--BridgeInterceptor--CacheInterceptor--ConnectInterceptor--networkInterceptors（非forWebSocket）--CallServerInterceptor加入到列表中。
	 * 接着构造一个拦截器链index=0的链元素RealInterceptorChain 最终调用第一个链元素的proceed
	 *
	 * 这个方法是通过拦截器链对请求数据和返回数据进行处理，内部采用责任链模式，将每一个拦截器对应负责的处理任务进行严格分配，最后将交易结果返回并回调到暴露给调用者的接口上。
	 * 这些拦截器包括：
	 * ①.用户自定义的拦截器
	 * ②.retryAndFollowUpInterceptor：重试和重定向拦截器，主要负责网络失败重连。
	 * ③.BridgeInterceptor：主要负责添加交易请求头。
	 * ④.CacheInterceptor：缓存拦截器，主要负责拦截缓存。
	 * ⑤.ConnectInterceptor：网络连接拦截器，主要负责正式开启http请求。
	 * ⑥.CallServerInterceptor：负责发送网络请求和读取网络响应。
	 *
	 * @return
	 * @throws IOException
	 */
	Response getResponseWithInterceptorChain() throws IOException {
		// Build a full stack of interceptors.
		List<Interceptor> interceptors = new ArrayList<>();
		// 添加客户拦截器
		interceptors.addAll(client.interceptors());
		interceptors.add(retryAndFollowUpInterceptor);
		interceptors.add(new BridgeInterceptor(client.cookieJar()));
		interceptors.add(new CacheInterceptor(client.internalCache()));
		interceptors.add(new ConnectInterceptor(client));
		if (!forWebSocket) {
			interceptors.addAll(client.networkInterceptors());
		}
		interceptors.add(new CallServerInterceptor(forWebSocket));

		Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest, this,
				eventListener, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis());

		Response response = chain.proceed(originalRequest);
		if (retryAndFollowUpInterceptor.isCanceled()) {
			closeQuietly(response);
			throw new IOException("Canceled");
		}
		return response;
	}
}
